#include "StdAfx.h"
#include "PrimitiveSphere.h"

using namespace std;

namespace FEngine
{
	PrimitiveSphere::PrimitiveSphere(float _radius, unsigned int _longitudeSub, unsigned int _latitudeSub)
		: Primitive()
	{
		// We will generate a sphere which vertex are placed at the intersection points of the longitude and latitude lines.
		// Except for the top and botom points of the sphere, we generate circles (on the Z plan) placed at the height of the latitude lines.
		// Each of those circles will have a different radius, based on the distance from the center of the sphere (on the Z axis).

		// Generate the vertices
		vector<Vector3> verticesVector;
		double angle = 0;
		double x, y;
		int subivisions_longitude = _longitudeSub < 3 ? 3 : _longitudeSub;
		int subivisions_latitude = _latitudeSub < 3 ? 3 : _latitudeSub;
		float rad;
		float height = _radius;
		float firstCircleHeight = ((_radius * 2) / (subivisions_latitude - 1)) / 4;
		
		// Top point of the sphere
		verticesVector.push_back(Vector3(0, _radius, 0));

		// Top circle of the sphere
		height = _radius - firstCircleHeight;
		for (int j = 0; j < subivisions_longitude; ++j)
		{
			angle = j * 360.0f / subivisions_longitude;
			rad = sqrt((_radius * _radius) - (height * height));
			x = cos(DEGREES_TO_RADIANS(angle)) * rad;
			y = sin(DEGREES_TO_RADIANS(angle)) * rad;
			verticesVector.push_back(Vector3((float)x, height, (float)y));
		}
		
		height = _radius;
		for (int i = 1; i < subivisions_latitude - 1; ++i)
		{
			height -= (_radius * 2) / (subivisions_latitude - 1);

			// Create one circle on the Z plane
			for (int j = 0; j < subivisions_longitude; ++j)
			{
				angle = j * 360.0f / subivisions_longitude;
				rad = sqrt((_radius * _radius) - (height * height));
				x = cos(DEGREES_TO_RADIANS(angle)) * rad;
				y = sin(DEGREES_TO_RADIANS(angle)) * rad;
				verticesVector.push_back(Vector3((float)x, height, (float)y));
			}
		}

		// Bottom circle of the sphere
		height = firstCircleHeight - _radius;
		for (int j = 0; j < subivisions_longitude; ++j)
		{
			angle = j * 360.0f / subivisions_longitude;
			rad = sqrt((_radius * _radius) - (height * height));
			x = cos(DEGREES_TO_RADIANS(angle)) * rad;
			y = sin(DEGREES_TO_RADIANS(angle)) * rad;
			verticesVector.push_back(Vector3((float)x, height, (float)y));
		}

		// Bottom point of the sphere
		verticesVector.push_back(Vector3(0, -_radius, 0));

		
		// Generate the indexes
		vector<unsigned int> indexesVector;
		int offset;
		
		// Link the top point with the first circle
		for (int i = 1; i < subivisions_longitude + 1; ++i)
		{
			indexesVector.push_back(0);

			// Link the last point of the circle to the first
			if (i == subivisions_longitude)
				indexesVector.push_back(1);
			else
				indexesVector.push_back(i + 1);

			indexesVector.push_back(i);
		}
		
		// Link the bottom point with the last circle
		offset = verticesVector.size() - subivisions_longitude - 1;
		for (int i = 0; i < subivisions_longitude; ++i)
		{
			indexesVector.push_back(offset + i);

			// Link the last point of the circle to the first
			if (i + 1 == subivisions_longitude)
				indexesVector.push_back(offset);
			else
				indexesVector.push_back(offset + i + 1);
			
			indexesVector.push_back(verticesVector.size() - 1);
		}
		
		// Link every circle to the circle placed under
		offset = 1;
		for (int i = 1; i < subivisions_latitude; ++i)
		{
			for (int j = 0; j < subivisions_longitude; ++j)
			{
				// First triangle
				indexesVector.push_back(offset + j);
				
				// Link the last point of the circle to the first
				if (j + 1 == subivisions_longitude)
					indexesVector.push_back(offset + subivisions_longitude);
				else
					indexesVector.push_back(offset + subivisions_longitude + j + 1);
				
				indexesVector.push_back(offset + subivisions_longitude + j);
				
				// Second triangle
				indexesVector.push_back(offset + j);
				
				// Link the last point of the circle to the first
				if (j + 1 == subivisions_longitude)
				{
					indexesVector.push_back(offset + j - subivisions_longitude + 1);
					indexesVector.push_back(offset + j + 1);
				}
				else
				{
					indexesVector.push_back(offset + j + 1);
					indexesVector.push_back(offset + subivisions_longitude + j + 1);
				}
			}
			offset += subivisions_longitude;
		}

		_createGeometry(verticesVector, indexesVector, D3DPT_TRIANGLELIST);
	}

	PrimitiveSphere::~PrimitiveSphere()
	{
	}

} // namespace FEngine
